﻿using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Runtime.CompilerServices;
using System.Linq;
using System.Reflection;
using System.Text.RegularExpressions;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Media;

namespace Dimok {
	public static class Extentions {

		public static ObservableCollection<T> Add<T>(this ObservableCollection<T> c, IEnumerable<T> items) {
			foreach (var item in items)
				c.Add(item);
			return c;
		}
		public static bool TryParse(this string s,out Guid guid ){
			try{
				guid = new Guid(s);
				return true;
			}catch{
				guid = new Guid();
				return false;
			}
		}
    public static Color ToColor(this string argb) {
      return Convert.ToUInt32(argb.Replace("#",""),16).ToColor();
    }
    public static Color ToColor(this uint argb) {
      return Color.FromArgb((byte)((argb & -16777216) >> 0x18),
      (byte)((argb & 0xff0000) >> 0x10),
      (byte)((argb & 0xff00) >> 8),
      (byte)(argb & 0xff));
    }

		#region GetChildren
		public static T GetChild<T>(this DependencyObject parent,int depth) where T : DependencyObject {
			if (depth <= 0) return null;
			if (parent is Panel) {
				foreach (var child in (parent as Panel).Children) {
					if (child is T) return child as T;
					var d = GetChild<T>(child,depth-1);
					if (d is T) return d;
				}
			} else if (parent is Grid) {
				foreach (var child in (parent as Grid).Children) {
					if (child is T) return child as T;
					var d = GetChild<T>(child, depth - 1);
					if (d is T) return d;
				}
			} else {
				try {
					var d = VisualTreeHelper.GetChild(parent, 0);
					for (int i = 1; d != null; i++) {
						if (d is T) return d as T;
						d = GetChild<T>(d, depth - 1);
						if (d == null) continue;
						if (d is T) return d as T;
						try {
							d = VisualTreeHelper.GetChild(d, i);
						} catch (ArgumentOutOfRangeException) {
							return null;
						}
					}
				} catch (ArgumentOutOfRangeException) {
					return null;
				}
			}
			return null;
		}
		public static List<DependencyObject> GetChildren<T>(this DependencyObject parent) where T : DependencyObject {
			var dos = new List<DependencyObject>();
			if (parent is Panel) {
				foreach (var child in (parent as Panel).Children) {
					if (child is T) dos.Add(child);
					dos.AddRange(GetChildren<T>(child));
				}
			} else {
				try {
					var d = VisualTreeHelper.GetChild(parent, 0);
					for (int i = 1; d != null; i++) {
						if (d is T) dos.Add(d);
						dos.AddRange(GetChildren<T>(d));
						try {
							d = VisualTreeHelper.GetChild(d, i);
						} catch (ArgumentOutOfRangeException) {
							return dos;
						}
					}
				} catch (ArgumentOutOfRangeException) {
					return dos;
				}
			}
			return dos;
		}
		#endregion

		public static T GetParent<T>(this DependencyObject d) where T : class {
			while ((d != null) && !(d is T))
				d = VisualTreeHelper.GetParent(d);

			//if (d == null)
			//  throw new Exception(d.GetType().Name + " element does not belong to " + typeof(T).Name);
			return d as T;
		}
		public static DependencyObject GetParent(this DependencyObject d,string typeName) {
			while ((d != null) && d.GetType().Name != typeName)
				d = VisualTreeHelper.GetParent(d);
			return d;
		}

		public static string RemoveSpacePreserve(this string text) {
			return text.Replace(" xml:space=\"preserve\"", "");
		}
		public static string MakeHeader(this string Name) {
			return Regex.Replace(Regex.Replace(Name, "([a-z]+)([A-Z])", "$1 $2"), "_", " ").Trim();
		}

		public static string Default(this string s) { return s.Default(null); }
		public static string Default(this string s, string DefaultValue) {
			return s.Default("", DefaultValue);
		}
		public static string Default(this string s, string NullIFValue, string DefaultValue) {
			return s + "" == NullIFValue ? DefaultValue : s;
		}
		public static T EnsureValue<T>(this Dictionary<string, T> Dic, string Key) where T : class {
			if (Dic.ContainsKey(Key)) return Dic[Key];
			Dic.Add(Key, Activator.CreateInstance<T>());
			return Dic[Key];
		}
		public static Dictionary<string, object> ToDictionary(this object o) {
			var d = o.GetType().GetProperties().ToDictionary(s => s.Name, s => o.GetProperty(s.Name));
			return d;
		}
		public static Dictionary<string, string> Trim(this Dictionary<string, string> d) {
			return d.Where(f => !string.IsNullOrEmpty(f.Value)).ToDictionary(f => f.Key, f => f.Value);
		}
		public static string AssembleyName<T>() {
			return typeof(T).AssembleyName();
		}
		public static string AssembleyName(this Type type) {
			return type.Assembly.FullName.Split(',')[0];
		}
		public static string AssembleyName(this object o) {
			return o.GetType().AssembleyName();
		}
		
		public static string GetBaseName(this Type t) {
			return string.Join(".", t.FullName.Split('.').Reverse().Skip(1).Reverse().ToArray());
		}
    public static Type GetBaseType(this Type t) {
      var subTypeName = Regex.Match(t.AssemblyQualifiedName, @"\[\[(.+)\]\]").Groups[1].Value;
      if (subTypeName != "") return Type.GetType(subTypeName);
      return t;
    }
		public static void Copy(this object Source, object Destination) {
			foreach (PropertyInfo pi in Source.GetType().GetProperties())
				Destination.SetProperty(pi.Name, Source.GetProperty(pi.Name));
		}
		public static string GetPropertyName(this object o, string CaseInsensitivePropertyName) {
			return GetPropertyName(o.GetType(), CaseInsensitivePropertyName, false);
		}
		/// <summary>
		/// Get properly capitalized property name
		/// </summary>
		/// <param name="CaseInsensitivePropertyName">Property Name</param>
		/// <param name="RaiseError">Default: false</param>
		/// <returns></returns>
		public static string GetPropertyName(this object o, string CaseInsensitivePropertyName, bool RaiseError) {
			return GetPropertyName(o.GetType(), CaseInsensitivePropertyName, RaiseError);
		}
		public static string GetPropertyName(this Type t, string CaseInsensitivePropertyName) {
			return GetPropertyName(t, CaseInsensitivePropertyName, false);
		}
		/// <summary>
		/// Get properly capitalized property name
		/// </summary>
		/// <param name="CaseInsensitivePropertyName">Property Name</param>
		/// <param name="RaiseError">Default: false</param>
		/// <returns></returns>
		public static string GetPropertyName(this Type t, string CaseInsensitivePropertyName, bool RaiseError) {
			var s = t.GetProperties().Select(p => p.Name).SingleOrDefault(n => n.ToLower() == CaseInsensitivePropertyName.ToLower()) + "";
			if (s == "" && RaiseError) throw new NotImplementedException("Type:" + t.FullName + ",Property:" + CaseInsensitivePropertyName);
			return s;
		}
    public static object GetPropertyOrNull(this object o, params string[] propertyNames) {
      object v = null;
      o.TryGetProperty(out v, propertyNames);
      return v;
    }
    public static bool TryGetProperty(this object o, out object v,params string[] propertyNames) {
      v = null;
      foreach (var p in propertyNames)
        if (o.TryGetProperty(p, out v)) return true;
      return false;
    }
		public static bool TryGetProperty(this object o, string p, out object v) {
			System.Reflection.PropertyInfo pi = o.GetType().GetProperty(p);
			if (pi != null) {
				v = pi.GetValue(o, null);
				return true;
			}
			System.Reflection.FieldInfo fi = o.GetType().GetField(p);
			if (fi != null) {
				v = fi.GetValue(o);
				return true;
			}
			v = null;
			return false;
		}

    public static object[] GetProperties(this object o) {
      var props = new List<object>();
      foreach (var p in o.GetType().GetProperties())
        props.Add(o.GetProperty(p.Name));
      return props.ToArray();
    }
    public static object GetProperty(this object o, string p) {
			System.Reflection.PropertyInfo pi = o.GetType().GetProperty(p);
			if (pi != null) return pi.GetValue(o, null);
			System.Reflection.FieldInfo fi = o.GetType().GetField(p);
			if (fi != null) return fi.GetValue(o);
			throw new NotImplementedException("Property/Field " + p + " is not implemented in " + o.GetType().Name + ".");
		}
		public static void SetProperty(this object o, string p, object v) {
			var convert = new Func<object, Type, object>((valie, type) => {
				if (valie != null) {
					Type tThis = Nullable.GetUnderlyingType(type);
					if (tThis == null) tThis = type;
					valie = Convert.ChangeType(v, tThis, null);
				}
				return valie;
			});
			System.Reflection.PropertyInfo pi = o.GetType().GetProperty(p);
			/*
			if (tThis == typeof(System.Double)) v = Convert.ToDouble(v);
			else if (tThis == typeof(System.String)) v = v + "";
			else if (tThis == typeof(System.DateTime)) v = Convert.ToDateTime(v);
			else if (tThis == typeof(System.Decimal)) v = Convert.ToDecimal(v);
			else if (tThis == typeof(System.Int32)) v = Convert.ToInt32(v);
			else if (tThis == typeof(System.Byte)) v = Convert.ToByte(v);
			*/
			if (pi != null) pi.SetValue(o, v = convert(v, pi.PropertyType), new object[] { });
			else {
				System.Reflection.FieldInfo fi = o.GetType().GetField(p);
				if (fi == null) throw new NotImplementedException("Property " + p + " is not implemented in " + o.GetType().FullName + ".");
				fi.SetValue(o, convert(v, fi.FieldType));
			}
		}
		//public static void SetProperty(this object o, string p, object v) {
		//  System.Reflection.PropertyInfo pi = o.GetType().GetProperty(p);
		//  if (pi != null) pi.SetValue(o, v, new object[] { });
		//  else {
		//    System.Reflection.FieldInfo fi = o.GetType().GetField(p);
		//    if (fi == null) throw new NotImplementedException("Property " + p + " is not implemented in " + o.GetType().FullName + ".");
		//    fi.SetValue(o, v);
		//  }
		//}
		public static int Levenshtein(this string s, string t) {
			int n = s.Length;
			int m = t.Length;
			int[,] d = new int[n + 1, m + 1];
			// Step 1
			if (n == 0) return m;
			if (m == 0) return n;

			// Step 2
			for (int i = 0; i <= n; d[i, 0] = i++) {
			}

			for (int j = 0; j <= m; d[0, j] = j++) {
			}

			// Step 3
			for (int i = 1; i <= n; i++) {
				//Step 4
				for (int j = 1; j <= m; j++) {
					// Step 5
					int cost = (t[j - 1] == s[i - 1]) ? 0 : 1;

					// Step 6
					d[i, j] = Math.Min(
							Math.Min(d[i - 1, j] + 1, d[i, j - 1] + 1),
							d[i - 1, j - 1] + cost);
				}
			}
			// Step 7
			return d[n, m];
		}
	}
}